﻿//+--------------------------------------------------------------------------------------------------
//     
//Description: Resource Definition .NET objects used in JSON Ser/Deserialization
//Author: Aravind Ramarathinam (aravr@microsoft.com)
//Copyright © Microsoft Corporation
//Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
//You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
//
//THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
//INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. 
//See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
// 
//--------------------------------------------------------------------------------------------------------------------------------------------------------


using System.Collections.Generic;
using System;
using System.Runtime.Serialization;
using System.Text;
using System.Web;
using System.Web.Script.Serialization;

namespace VmroleAuthor.Models
{

    #region StringHolder
    public class StringHolder
    {
        public string Value;
    }
    #endregion


    public class EnumHelper
    {
        // This is needed because Enum.TryParse silently accepts string integer numerals
        public static bool TryParse<TEnumType>(string str, bool caseInSensitive, out TEnumType value) where TEnumType : struct
        {
            if (!typeof(TEnumType).IsEnum)
            {
                throw new ArgumentException("Must be enum type");
            }            
            foreach (var name in Enum.GetNames(typeof(TEnumType)))
            {
                if (String.Equals(name, str, caseInSensitive ? StringComparison.OrdinalIgnoreCase: StringComparison.Ordinal ))
                {
                    value = (TEnumType)Enum.Parse(typeof(TEnumType), name);
                    return true;
                }
            }
            value = default(TEnumType);
            return false;
        }
    }

    [Serializable]
    [DataContract]
    public enum ProvisioningState
    {
        [EnumMember]
        Provisioning = 1,
        [EnumMember]
        Provisioned = 2,
        [EnumMember]
        Updating = 3,
        [EnumMember]
        Failed = 4,
        [EnumMember]
        Deprovisioning = 5,
    }

    [DataContract]
    public class ResourceDefinition
    {
        public const string ExpectedSchemaVersion = "1.0";
        public const string ExpectedType = "Microsoft.Compute/VMRole/1.0";

        private string name;
        private string publisher;

        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public string Name
        {
            // Need a default value if not specified, since ResDef Name is used in all error messages
            get { return this.name ?? "UnspecifiedName";  }
            set { this.name = value; }
        }
        
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string Version { get; set; }
        
        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public string Publisher
        {
            // Need a default value if not specified
            get { return this.publisher ?? "UnspecifiedPublisher"; }
            set { this.publisher = value; }          
        }

        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string SchemaVersion { get; set; }

        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string Type { get; set; }

        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public List<Parameter> ResourceParameters { get; set; }
        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public List<ResourceExtensionReference> ResourceExtensionReferences { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public IntrinsicSettings IntrinsicSettings { get; set; }      
    }
    [DataContract]
    public class IntrinsicSettings
    {       
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public ScaleOutSettings ScaleOutSettings { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public VMHardwareProfile HardwareProfile { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public VMStorageProfile StorageProfile { get; set; }
        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public OperatingSystemProfile OperatingSystemProfile { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public VMNetworkProfile NetworkProfile { get; set; }
    }

    #region Resource Parameter
    public enum ParameterType
    {
        String,
        Int,
        Boolean,
        Credential,
        SecureString
    }

    [DataContract]
    public class Parameter
    {
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string Name { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string Type { get; set; }
        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public string Description { get; set; }
    }
    #endregion Resource Parameter

    #region Resource Extension Reference

    public class ResourceExtensionParameterHolder
    {
        public string ParameterName;
        public string ParameterValue;
    }



    [DataContract]
    public class ResourceExtensionReference
    {
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string ReferenceName { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string Name { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string Version { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string Publisher { get; set; }
        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public string ResourceExtensionParameterValues { get; set; }

        private List<ResourceExtensionParameterHolder> resourceExtensionParameterHolders;

        [ScriptIgnore]
        public List<ResourceExtensionParameterHolder>  ResourceExtensionParameterHolders
        {
            get
            {
                if (this.resourceExtensionParameterHolders == null)
                {
                    this.resourceExtensionParameterHolders = new List<ResourceExtensionParameterHolder>();
                    if (this.ResourceExtensionParameterValues != null)
                    {
                        JavaScriptSerializer ser = new JavaScriptSerializer();                        
                        Dictionary<string, object> objectJsonDom = ser.DeserializeObject(this.ResourceExtensionParameterValues) as Dictionary<string, object>;
                        if (objectJsonDom != null)
                        {
                            foreach (KeyValuePair<string, object> kvp in objectJsonDom)
                            {
                                var resExtParamHolder = new ResourceExtensionParameterHolder
                                    {
                                        ParameterName = kvp.Key,
                                        ParameterValue = kvp.Value as string
                                    };
                                this.resourceExtensionParameterHolders.Add(resExtParamHolder);
                            }
                        }    
                    }                    
                }
                return this.resourceExtensionParameterHolders;
            }
            set { this.resourceExtensionParameterHolders = value; }
        }

        [OnSerializing]
        private void SetValuesOnSerializing(StreamingContext context)
        {   
            if (this.ResourceExtensionParameterHolders == null || this.ResourceExtensionParameterHolders.Count == 0)
            {
                this.ResourceExtensionParameterValues = null;
                return;
            }
            this.SetResourceExtensionParameterValues();
        }

        public void SetResourceExtensionParameterValues()
        {
             if (this.ResourceExtensionParameterHolders == null || this.ResourceExtensionParameterHolders.Count == 0)
             {
                 this.ResourceExtensionParameterValues = null;
                 return;
             }
            bool prependCommand = false;
            StringBuilder sb = new StringBuilder();
            sb.Append("{");
            foreach (ResourceExtensionParameterHolder resourceExtensionParameterHolder in this.ResourceExtensionParameterHolders)
            {
                if (string.IsNullOrEmpty(resourceExtensionParameterHolder.ParameterName))
                {
                    continue;
                }
                string paramName = HttpUtility.JavaScriptStringEncode(resourceExtensionParameterHolder.ParameterName);
                string paramValue = HttpUtility.JavaScriptStringEncode(resourceExtensionParameterHolder.ParameterValue);
                if (prependCommand)
                {
                    sb.Append(",");
                }
                string resExtParamValue = string.Format("\"{0}\":\"{1}\"", paramName, paramValue);
                sb.Append(resExtParamValue);
                prependCommand = true;
            }
            sb.Append("}");
            this.ResourceExtensionParameterValues = sb.ToString();
        }

    }   
    #endregion Resource Extension Reference    

    #region ScaleOut Settings
    [DataContract]
    public class ScaleOutSettings
    {
        [DataMember(IsRequired = true)]
        public string InitialInstanceCount { get; set; }
        [DataMember(IsRequired = true)]
        public string MaximumInstanceCount { get; set; }
        [DataMember(IsRequired = true)]
        public string MinimumInstanceCount { get; set; }
        [DataMember(IsRequired = true)]
        public string UpgradeDomainCount { get; set; }
    }
    #endregion ScaleOut Settings
   
    #region HardwareProfile
    public enum RoleSize
    {
        ExtraSmall,
        Small,
        Medium,
        Large,
        ExtraLarge,
        A6,
        A7
    }
    [DataContract]
    public class VMHardwareProfile 
    {       
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string VMSize { get; set; } 
    }
    #endregion VMHardwareProfile

    #region StorageProfile
    [DataContract]
    public class DataVirtualHardDisk
    {
        [DataMember(IsRequired = true, EmitDefaultValue = false, Name = "DataVirtualHardDiskImage")]
        public string DataVirtualHardDiskImage { get; set; }
        [DataMember(IsRequired = true)]
        public string Lun { get; set; } //int
    }
    [DataContract]
    public class VMStorageProfile
    {
        [DataMember(IsRequired = true, EmitDefaultValue = false, Name = "OSVirtualHardDiskImage")]
        public string OSVirtualHardDiskImage { get; set; }
        [DataMember(IsRequired = false, EmitDefaultValue = false, Name = "DataVirtualHardDisks")]
        public List<DataVirtualHardDisk> DataVirtualHardDisks { get; set; }
    }
    #endregion StorageProfile

    #region NetworkProfile
    [DataContract]
    public class PortConfig 
    {
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string Name { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string Protocol { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string FrontEndPort { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string BackEndPort { get; set; }    
    }
    [DataContract]
    public class Probe 
    {
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string ProbeIntervalInSeconds { get; set; }    
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string Protocol { get; set; }     
        [DataMember(IsRequired = true)]
        public string Port { get; set; }
        [DataMember(IsRequired = true)]
        public string MaxRetryCount { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string RequestPath { get; set; }       
    }
    [DataContract]
    public class LoadBalancerConfiguration 
    {
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string ConfigurationName { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string FrontEndNetwork { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public PortConfig PortConfig { get; set; }
        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public Probe Probe { get; set; }
    }

    public enum IPAllocationMethod
    {
        Static,
        Dynamic,
    }
    public enum IpProtocolVersion
    {
        IPV4,
        IPV6,
    }
    public enum PortCfgProtocol
    {
        Http,
        Https,
    }
    public enum LbCfgProbeProtocol
    {
        Http,
        Tcp,
        Icmp,
    }

    [DataContract]
    public class IPAddress 
    {
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string AllocationMethod { get; set; }

        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string Type { get; set; }

        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string ConfigurationName { get; set; }

        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public List<LoadBalancerConfiguration> LoadBalancerConfigurations { get; set; }   
    }

    [DataContract]
    public class NetworkAdapter 
    {
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string Name { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string NetworkRef { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public List<IPAddress> IPAddresses { get; set; }
    }

    [DataContract]
    public class VMNetworkProfile
    {
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public List<NetworkAdapter> NetworkAdapters { get; set; }
    }
    #endregion NetworkProfile

    #region OperatingSystemProfile    
    [DataContract]
    public class OperatingSystemProfile 
    {       
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string ComputerNamePattern { get; set; } // e.g., "MyComputer###"               
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string TimeZone { get; set; } //tzUtil or HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones 
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string AdminCredential { get; set; }        
        [DataMember]
        public WindowsOperatingSystemProfile WindowsOperatingSystemProfile { get; set; }
        [DataMember]
        public LinuxOperatingSystemProfile LinuxOperatingSystemProfile { get; set; }
    }
    
    [DataContract]
    public class WindowsOperatingSystemProfile 
    {
        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public string WorkgroupName { get; set; }             
        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public DomainJoin DomainJoin { get; set; }
    }   
    [DataContract]
    public class DomainJoin
    {
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string DomainToJoin { get; set; }
        [DataMember(IsRequired = true, EmitDefaultValue = false)]
        public string DomainJoinCredentials { get; set; }
    }

    [DataContract]
    public class LinuxOperatingSystemProfile 
    {
        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public string DNSDomainName { get; set; }
        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public string SSHPublicKey { get; set; }
    }
    #endregion OperatingSystemProfile   
}
